# Port scaffold of mod_dates.f90

module DatesUtil

"""
    juldate(yyyymmdd::Int, hhmiss::Int) -> Float64

Fortran-equivalent Julian date computation from mod_dates.f90.
"""
function juldate(yyyymmdd::Int, hhmiss::Int)
    yyyy = yyyymmdd ÷ 10000
    mm   = (yyyymmdd - 10000*yyyy) ÷ 100
    dd   = yyyymmdd - 10000*yyyy - 100*mm
    hh   = hhmiss ÷ 10000
    mi   = (hhmiss - 10000*hh) ÷ 100
    ss   = hhmiss - 10000*hh - 100*mi

    yyyy == 0 && error("juldate: there is no year zero")
    if yyyy < 0
        yyyy += 1
    end
    jy = mm > 2 ? yyyy : (yyyy - 1)
    jm = mm > 2 ? (mm + 1) : (mm + 13)
    julday = Int(floor(365.25*jy)) + Int(floor(30.6001*jm)) + dd + 1720995
    igreg = 15 + 31*(10 + 12*1582)
    if (dd + 31*(mm + 12*yyyy)) >= igreg
        ja = Int(floor(0.01*jy))
        julday = julday + 2 - ja + Int(floor(0.25*ja))
    end
    return float(julday) + float(hh)/24 + float(mi)/1440 + float(ss)/86400
end

"""
    caldate(jdate::Float64) -> (yyyymmdd::Int, hhmiss::Int)

Inverse of juldate using the Fortran algorithm for exact parity.
"""
function caldate(jdate::Float64)
    julday = floor(Int, jdate)
    igreg = 2299161
    ja = if julday >= igreg
        jalpha = floor(Int, ((julday - 1867216) - 0.25) / 36524.25)
        julday + 1 + jalpha - floor(Int, 0.25*jalpha)
    else
        julday
    end
    jb = ja + 1524
    jc = floor(Int, 6680.0 + ((jb - 2439870) - 122.1) / 365.25)
    jd = 365*jc + floor(Int, 0.25*jc)
    je = floor(Int, (jb - jd) / 30.6001)
    dd = jb - jd - floor(Int, 30.6001*je)
    mm = je - 1
    mm > 12 && (mm -= 12)
    yyyy = jc - 4715
    mm > 2 && (yyyy -= 1)
    yyyy <= 0 && (yyyy -= 1)

    yyyymmdd = 10000*yyyy + 100*mm + dd
    frac = jdate - float(julday)
    hh = Int(floor(24 * frac + 1e-9))
    mi = Int(floor(1440 * frac - 60*hh + 1e-9))
    ss = round(Int, 86400 * frac - 3600*hh - 60*mi)
    if ss == 60
        ss = 0; mi += 1
    end
    if mi == 60
        mi = 0; hh += 1
    end
    hhmiss = 10000*hh + 100*mi + ss
    return yyyymmdd, hhmiss
end

"""
    calceomday(yyyymm::Int) -> Int

Days in month (Gregorian), matching Fortran logic.
"""
function calceomday(yyyymm::Int)
    yyyy = yyyymm ÷ 100
    mm = yyyymm - 100*yyyy
    leapdays = (31,29,31,30,31,30,31,31,30,31,30,31)
    days     = (31,28,31,30,31,30,31,31,30,31,30,31)
    if (float(yyyy)/100) == (yyyy ÷ 100)
        ((float(yyyy)/400) == (yyyy ÷ 400)) ? leapdays[mm] : days[mm]
    else
        ((float(yyyy)/4) == (yyyy ÷ 4)) ? leapdays[mm] : days[mm]
    end
end

end # module
